//: Playground - noun: a place where people can play

import UIKit

func sumNumber(numbers: Int...) -> Int{
    
    var  total = 0
    for number in numbers {
        
        total += number
    }
    return  total
}

let sum = sumNumber(2,3,5,6)
print(sumNumber(2,3,5,6))


let  fallthoughSwithch = 10

switch fallthoughSwithch{
    
case 0..<20:
    print("number is between  0 and 20")
case 0..<30:
    print("number is betwwen 0 and 30")

default:
    print("number is else")
    
}

var  anInteger = 2


let emptyString  = ""

let anotherEmptyString = String()


emptyString.isEmpty


var composingAlString = "hello"


composingAlString += ", World"


var resverdString  = ""


for character in "hello".characters{
    
    resverdString = String(character) + resverdString
    
}
resverdString


"hello".characters.count

resverdString.uppercaseString
resverdString.lowercaseString



let string1 : String = "Hello"

let string2 : String = "Hel" + "lo"


if string1 == string2{
    print("the string are equual")
}


if string1.hasPrefix("H")
{
    print("String begin with an H")
}

if string1.hasSuffix("llo"){
    print("string ends in llo")
}

var  setOfString = Set<String>()

var fruitSet:Set = ["apple","orange","orange", "banana"]

fruitSet.count

if  fruitSet.isEmpty{
    print("fruitSet isEmpty")
}


fruitSet.remove("banana")
fruitSet.insert("pear")


//using Functions as Vairables
func addNumber(firstNamuber num1: Int , toSecoundNumber num2: Int)-> Int {
    return num1 + num2
}


var  numbersFunc:(Int , Int)->Int;

      numbersFunc = addNumber
    
//      numbersFunc(2,123)


func  timesTree(number :Int )-> Int{
    return  number * 3
}


func  doSomethingToNumber(aNumber:Int, thingToDo:(Int)->Int)->Int{
    
    return thingToDo(aNumber)
}
// timesTree as thingToDo
doSomethingToNumber(9, thingToDo: timesTree)


//This function takes an Int as Parameter , it returns a new Functions that takes a Int paramerter and return an Int

func creatAdder(numberToAdd: Int)->(Int)->Int{
    
    func adder(number :Int)-> Int{
        return number + numberToAdd
    }
    return adder
}

var addTwo  = creatAdder(6)

//addTwo(2)



func  createIncrementor(incrementAmount :Int)->()->Int{
    var amount  = 0
    func incrementor()->Int{
        amount += incrementAmount
        return amount
    }
    
    return incrementor
    
}


var incrementByTen = createIncrementor(10)

incrementByTen()
incrementByTen()


var incrementByFifteen = createIncrementor(15)

incrementByFifteen()


var sortingInline = [2,5,9,2,14]
sortingInline.sort()

var aNumbers=[2,130,1]

var aNumbersSorted = aNumbers.sort({
    (n1:Int,n2:Int)->Bool in return n2>n1

})

var  numbersSortedReverse = aNumbers.sort({n1,n2 in return n1 > n2})

numbersSortedReverse


var numbersSorteAgain = aNumbers.sort(
    {$1<$0}
)

var comparator = {(a: Int , b: Int) in a < b}

comparator(1,2)


func doSomeWork(){
    print("Getting started!")
    
    defer{
        print("all done")
    }
    print("getting to work")


}

doSomeWork()

//guard 2+2 == 4 else{
//    print("the unvierse make no sense")
//    return 1
//}
//
//print("we can continue with our daily life")



class vehicle {
    
    var color : String?
    var  maxSpeed = 80
    
    func descpriton() -> String {
        return "A \(self.color) vehicle"
    }
    
    func traver() {
        print("Traverling at \(maxSpeed) kph")
    }
    
}

var redVehicle = vehicle()

redVehicle.color = "red"
redVehicle.maxSpeed = 90

redVehicle.descpriton()


redVehicle.traver()


class InitAndDeinitExample {
    
    //Designated (i,e. main)initalizer
    init(){
        print("I'v been created")
    }
    
    convenience init ? (value:Int){
        self.init()
        
        if  value > 5 {
          return nil
        }
        print("I was called with the convenience initialzer!")
    }
    
    deinit{
        
        print("I m going a way")
    }
}

var example :InitAndDeinitExample?

//example = InitAndDeinitExample()



example = nil


//using the convenience initializer
example = InitAndDeinitExample(value :4)

class Counter {
    var number: Int = 0
    
}

let myCounter = Counter()

myCounter.number = 2



class car: vehicle {
    var engineType : String = "v8"
    
    override func descpriton() -> String {
        let descpriton = super.descpriton()
        return descpriton +  "which is car"
    }
}

var  baoma = car()


baoma.descpriton()

class Rectangle {
    var width:Double = 0.0
    var height: Double = 0.0
    var area:Double{
        get{
            return width * height
        }
    
        set {
            width  = sqrt(newValue)
            height = sqrt(newValue)
        }
    }
}

var rectangle = Rectangle()

rectangle.width = 30
rectangle.height = 90
rectangle.area

rectangle.area = 29

class PropertyObserverExample{
    var number : Int = 0{
        willSet(newNumber){
            print("about to change to \(newNumber)")
        }
        didSet(oldNumber){
            print("Just change from\(oldNumber) to \(self.number)")
        }
    }
    
}


var observer  = PropertyObserverExample()

observer.number = 4

class SomeExpensiveClass {
    init(id : Int){
        print("Expensive class\(id) created")
    }
   
}
class LazyPropertyExample {
    var expensiveClass1 = SomeExpensiveClass(id: 1)
    lazy var expensiveClass2 = SomeExpensiveClass(id: 2)
    init(){
        print("fist class created")
    }
    
}

var lazyExmple = LazyPropertyExample()
lazyExmple.expensiveClass1
lazyExmple.expensiveClass2



protocol Blinking{
    
    var  isBlinking :Bool{get}
    
    var  blinkSpeed:Double{get set}
    
    func startBlinking(blinkSpeed:Double)->Void
    
}

class TafficLight: Blinking {
    var isBlinking: Bool = false
    var blinkSpeed: Double = 0.0
    
    func startBlinking(blinkSpeed: Double) {
        print("I am a traffic light, and I am now blinking")
        isBlinking = true
        self.blinkSpeed = blinkSpeed
    }
    
}



class Lighthouse: Blinking {
    var   isBlinking: Bool = false
    var blinkSpeed: Double = 0.0
    
    func startBlinking(blinkSpeed: Double) {
        print("I am Lighthouse and im now blinking")
        isBlinking = true
        self.blinkSpeed = blinkSpeed
    }
    
}



var aBlinkingThing : Blinking
//can be any object that has the blinking the protocol

aBlinkingThing = TafficLight()

aBlinkingThing.startBlinking(4.0)
aBlinkingThing.blinkSpeed

aBlinkingThing = Lighthouse()



extension Int{
    var  doubled :Int{
        return self * 2
    }
    func muliplayWith(anotherNumber: Int) -> Int {
        return self * anotherNumber
    }
}

2.doubled
4.muliplayWith(7)


extension Int :Blinking{
    
    var isBlinking:Bool{
        get {
            return false
        }
    }
    
    var blinkSpeed :Double{
        get{
            return 0.0
        }
        set {
            
        }
    }
    
    func startBlinking(blinkSpeed: Double) {
        print("I am the integer \(self). I do not blink")
    }
}



2.isBlinking
2.startBlinking(3)



public class AccessControl{
    
    // Accessible to this module only
    // internal here is the default and can be omitted
    internal var internalProperty = 123
    //Accessible to everyone
    public   var publicProperty = 123
    
    //Only accessible in this source file
    private  var  privateProperty = 123
    //tht setter is private  so other files can't modify it
    private(set) var privateSetterProperty = 123
    
}



var  access = AccessControl()

access.internalProperty = 222


func +(left: Int ,right: Int) -> Int {
    return left + right
}

class Vector2D {
    var x : Float = 0.0
    var y : Float = 0.0
    init(x:Float, y:Float){
        self.x = x
        self.y = y
    }
  
}




func +(left :Vector2D, right: Vector2D) ->Vector2D {
    let  result = Vector2D(x: left.x + right.x, y: left.y + right.y)
    return result
}

let first = Vector2D(x: 2, y: 2)
let second = Vector2D(x: 4, y: 1)

let result = first + second


class Tree<T>{
    
    // 'T' can now be used as a type inside this class
     //'value' is of T
    
    var value :T
    private(set) var children :[Tree<T>] = []
    
    init(value :T){
        self.value = value
    }
    //And we can add a child node to our list of children 
    func addChild(value :T)->Tree<T>{
        let newChild = Tree<T>(value: value)
        
        children.append(newChild)
        return newChild
    }
    
}

//Tree of integers


let intergerTree = Tree<Int>(value :5)
intergerTree.addChild(10)
intergerTree.addChild(5)


//tree of strings


let stringTree = Tree<String>(value: "hello")

stringTree.addChild("yes")

stringTree.addChild("internets")




//Subscripts



extension UInt8 {
    //allow subscripts this type using uint8
    subscript(bit: UInt8)->UInt8{
        //this is  run when you do thing like vale[x]
        get{
            return (self>>bit & 0x07) & 1
            
        }
        //this is run when you do thing like vlaue[x] =y
        set {
            let cleanBit = bit & 0x07
            let  mask = 0xFF ^ (1<<cleanBit)
            
            let shifteBit = (newValue & 1) << cleanBit
            self = self & mask | shifteBit
        }
    }
    
}

var  byte :UInt8 = 212

byte[0]
byte[1]
byte[2]
byte[5]



byte[7] = 0


byte


//Data

let  stringToConvert = "Hello swift"

let data  =  stringToConvert.dataUsingEncoding(NSUTF8StringEncoding)




//load from URL


if let  URL = NSURL(string: "https://oreilly.com"){
    let loadDataFromURL = NSData(contentsOfURL: URL)
}


//loading from a file


if let filepath = NSBundle.mainBundle().pathForResource("Log", ofType: "rtf"){
    let loadFromFile = NSData(contentsOfFile: filepath)
}
//Serialization and Deserializaiton


class SerializableObject:NSObject,NSCoding{
    var name:String?
    func encodeWithCoder(aCoder: NSCoder) {
        aCoder.encodeObject(name!,forKey: "name")
        
    }
    
    override init() {
        self.name = "My Object"
    }
    required init?(coder aDecoder: NSCoder) {
        self.name = aDecoder.decodeObjectForKey("name") as? String
    }
}

let anObject  = SerializableObject()

anObject.name = "my thing that I'm saving"

//Convert it to data

let  objcetConvertToData = NSKeyedArchiver.archivedDataWithRootObject(anObject)

//Converting it  back

let loadObject = NSKeyedUnarchiver.unarchiveObjectWithData(objcetConvertToData) as? SerializableObject

loadObject?.name


enum BankError:ErrorType {
    //Not enought money in account
    case  NotEnoughFunds
    
    //can't create an account with negative money
    
    case  CannotBegainWithNegativeFunds
    
    //Can't make a negative desposit or withrawal
    
    case CannotMakeNegavieTransactition(amount:Float)
}








